package com.ashin.plugin;

import com.ashin.bean.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;

/**
 * @Author: Ashin
 * @Date: 2020/9/2
 * @Description: com.juggernaut.plugin
 * @Version: 1.0.0
 */
@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class ParameterInterceptor implements Interceptor {


    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        Object parameterObject = invocation.getArgs()[1];


        Object[] clones = invocation.getArgs().clone();

        MappedStatement ms = (MappedStatement) invocation.getArgs()[0];


        if (Objects.nonNull(parameterObject)) {
            Class<?> parameterObjectClass = parameterObject.getClass();
            if (parameterObjectClass.isAssignableFrom(MapperMethod.ParamMap.class)) {
                MapperMethod.ParamMap paramMap = (MapperMethod.ParamMap) parameterObject;
                Set<Map.Entry<String, Object>> set = paramMap.entrySet();
                String id = ms.getId();
                String className = id.substring(0, id.lastIndexOf("."));
                String methodName = id.substring(id.lastIndexOf(".") + 1);

                //这里使用循环获取方法，映射器的方法名是不可能重复
                Class<?> aClass = Class.forName(className);
                Method aMethod = null;
                Method[] methods = aClass.getMethods();
                for (Method method : methods) {
                    if (methodName.equals(method.getName())) {
                        aMethod = method;
                        break;
                    }
                }

                Parameter[] parameters = aMethod.getParameters();
                for (Parameter parameter : parameters) {
                    boolean annotationPresent = parameter.isAnnotationPresent(EncryptDecrypt.class);
                    if (annotationPresent) {
                        Param annotation = parameter.getAnnotation(Param.class);
                        paramMap.put(annotation.value(), "$$");
                    }
                }


            } else {
                Field[] fields = parameterObjectClass.getDeclaredFields();
                for (Field field : fields) {
                    boolean annotationPresent = field.isAnnotationPresent(EncryptDecrypt.class);
                    if (annotationPresent) {
                        field.setAccessible(true);
                        field.set(parameterObject, "***");
                        field.setAccessible(false);
                    }
                }
            }

        }

        Object proceed = invocation.proceed();

        Field args = invocation.getClass().getDeclaredField("args");
        args.setAccessible(true);
        clones[1]=new User();
        args.set(invocation, clones);
        args.setAccessible(false);

        return proceed;
    }

    @Override
    public Object plugin(Object o) {
        return Plugin.wrap(o, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }
}